﻿using System;
using System.Collections;

namespace ERP.Framework.Components.Utilities.CloningHelper
{
    public abstract class CloningHelper
    {
        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <remarks>
        /// This function will clone all Properties in the object,
        /// it will ignore Indexers and objects without default constructor.
        /// Handles value types, lists and dictionaries
        /// </remarks>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        public static object Clone(object objToClone)
        {
            //create new object
            object clonedObject = null;
            //value type objects
            if (objToClone.GetType().GetConstructor(Type.EmptyTypes) == null || objToClone is ValueType)
            {
                clonedObject = objToClone;
            }//reference type objects
            else
            {
                clonedObject = ERP.Framework.Components.Utilities.TypeHelper.TypeFactory.CreateInstance(objToClone.GetType().AssemblyQualifiedName);
                Type objType = clonedObject.GetType();
                foreach (System.Reflection.PropertyInfo item in objType.GetProperties())
                {
                    bool isIndexer = item.GetIndexParameters().Length > 0;
                    Type propType = item.PropertyType;

                    if (item.CanWrite && !isIndexer)//Set writable props
                    {
                        object value = item.GetValue(objToClone, null);
                        object clonedValue = null;

                        if (value is IList)
                        {
                            clonedValue = ((IList)objType.GetProperty(item.Name).GetValue(clonedObject, null));
                            foreach (object lstItem in ((IList)value))
                            {
                                ((IList)clonedValue).Add(Clone(lstItem));
                            }
                        }
                        else if (value is IDictionary)
                        {
                            clonedValue = ((IDictionary)objType.GetProperty(item.Name).GetValue(clonedObject, null));
                            foreach (object lstItm in ((IDictionary)value))
                            {
                                ((IDictionary)clonedValue).Add(Clone(((DictionaryEntry)lstItm).Key),
                                    Clone(((DictionaryEntry)lstItm).Value));
                            }
                        }
                        else//Normal props
                        {
                            clonedValue = Clone(item.GetValue(objToClone, null));
                        }
                        objType.GetProperty(item.Name).SetValue(clonedObject, clonedValue, null);
                    }
                }
            }
            return clonedObject;
        }
    }
}
